package http

import (
	"encoding/json"
	"gitee.com/captials-team/ubdframe/src/common"
	"gitee.com/captials-team/ubdframe/src/common/consts"
	"gitee.com/captials-team/ubdframe/src/common/utils"
	"gitee.com/captials-team/ubdframe/src/domain/configstc"
	"gitee.com/captials-team/ubdframe/src/domain/converts"
	"gitee.com/captials-team/ubdframe/src/domain/dto"
	"gitee.com/captials-team/ubdframe/src/domain/dto/paginate"
	"gitee.com/captials-team/ubdframe/src/domain/dto/reqdata"
	"gitee.com/captials-team/ubdframe/src/domain/dto/respdata"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gitee.com/captials-team/ubdframe/src/domain/models"
	mysqlDao "gitee.com/captials-team/ubdframe/src/infrastructure/dao/mysql"
	"gitee.com/captials-team/ubdframe/src/pkg/gin_http"
	v1log "gitee.com/captials-team/ubdframe/src/pkg/logs"
	"github.com/gin-gonic/gin"
	"go.uber.org/dig"
	"gorm.io/gorm"
	"os"
	"strings"
)

type RbacController struct {
	l         v1log.ILog
	conf      *configstc.AdminAppConfig
	rbacModel interfaces.ItfRbac

	//权限列表
	permissions []*dto.PermissionItem

	gin_http.ResponseController
}

func NewRbacController(di *dig.Scope, l v1log.ILog, conf *configstc.AdminAppConfig) *RbacController {
	ctr := &RbacController{
		l:         l,
		rbacModel: mysqlDao.NewRbacDao(conf.DBConfig),
		conf:      conf,
	}

	{
		var err error
		_, err = ctr.rbacModel.Migrate()
		common.ErrPanic(err)
	}

	ctr.initPermission()

	return ctr
}

func (ctr *RbacController) initPermission() {
	var perms = []*dto.PermissionItem{
		{Id: 1, Name: "所有权限"},
	}
	if ctr.conf.PermissionFile != "" {
		perms = ctr.loadPermissionFromFile(ctr.conf.PermissionFile)
	}

	//空权限且配置了权限文件则进行初始化
	if list, _ := ctr.rbacModel.Perms(); len(list) == 0 && len(perms) > 0 {
		addPerms := utils.CopyMoreTo(perms, []*models.RbacPermission{})

		err := ctr.rbacModel.DB().Transaction(func(tx *gorm.DB) error {
			_, err := ctr.rbacModel.Use(tx).BatchAddPerm(addPerms...)
			return err
		})
		common.ErrPanic(err)
	}
	ctr.l.Info("load permissions num=%d", len(perms))
	ctr.permissions = perms
}

func (ctr *RbacController) loadPermissionFromFile(file string) []*dto.PermissionItem {
	var perms = []*dto.PermissionItem{}
	s, err := os.ReadFile(file)
	common.ErrPanic(err)
	err = json.Unmarshal(s, &perms)
	common.ErrPanic(err)
	return perms
}

// AccreditInfo godoc
// @Summary      账户中心-授权信息
// @Description  返回授权权限清单
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @success 200  {object} respdata.ResponseData{data=[]dto.RbacPermissionSimple{}} "返回数据"
// @response 500 {object} respdata.ResponseData{} "查询失败"
// @Router       /mag/u_center/accredit_info [post]
func (ctr *RbacController) AccreditInfo(ctx *gin.Context) {
	auth := gin_http.GetAuth(ctx)

	var hasPerms []*dto.RbacPermissionSimple
	//针对超级管理员返回所有
	if configstc.CommonExtendParams(auth.Extends).ExtendParamByBool("is_root") {
		for _, v := range ctr.permissions {
			hasPerms = append(hasPerms, utils.CopyTo(v, new(dto.RbacPermissionSimple)))
		}
		ctr.Response(ctx, respdata.CSuccess.MData(hasPerms))
		return
	}

	//查询对应用户权限节点
	permIds, err := ctr.rbacModel.UserPermIds(auth.Id)
	if err != nil {
		ctr.l.Error("query err %s", err)
		ctr.Response(ctx, respdata.CError)
		return
	}
	m := utils.SliceInt64ToMap(permIds)

	for _, v := range ctr.permissions {
		if _, exist := m[v.Id]; exist {
			hasPerms = append(hasPerms, utils.CopyTo(v, new(dto.RbacPermissionSimple)))
		}
	}

	ctr.Response(ctx, respdata.CSuccess.MData(hasPerms))
}

// SearchUserRoles godoc
// @Summary      角色管理-用户关联角色列表
// @Description  修改个人信息
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.DetailReq   false  "指定用户id"
// @success 200  {object} respdata.ResponseData{data=[]int64{}} "返回数据"
// @success 500 {object} respdata.ResponseData{} "查询失败"
// @Router       /mag/rbac/user/role/search [post]
func (ctr *RbacController) SearchUserRoles(ctx *gin.Context) {
	var req reqdata.DetailReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}
	list, err := ctr.rbacModel.UserRoleIds(req.Id)
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.Response(ctx, respdata.CSuccess.MData(list))
}

// BindUserRoles godoc
// @Summary      角色管理-用户绑定关联角色
// @Description  修改个人信息
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.SaveUserBindRoleReq   false  "提交参数"
// @success 200  {object} respdata.ResponseData{data=[]models.RbacRole{}} "返回数据"
// @success 500 {object} respdata.ResponseData{} "查询失败"
// @Router       /mag/rbac/user/role/bind [post]
func (ctr *RbacController) BindUserRoles(ctx *gin.Context) {
	var req reqdata.SaveUserBindRoleReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	err := ctr.rbacModel.UserRelateRoles(req.Id, req.Roles)
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.Response(ctx, respdata.CSuccess)
}

// SearchRoles godoc
// @Summary      角色管理-角色列表
// @Description  修改个人信息
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.SearchRolesReq   false  "查询参数,is_tree暂不支持"
// @success 200  {object} respdata.ResponseData{data=respdata.SearchListResponse{}} "返回数据"
// @success 200  {object} respdata.ResponseData{data=[]models.RbacRole{}} "List返回数据"
// @failure 500 {object} respdata.ResponseData{} "返回失败"
// @Router       /mag/rbac/role/search [post]
func (ctr *RbacController) SearchRoles(ctx *gin.Context) {
	var req reqdata.SearchRolesReq
	ctx.ShouldBind(&req)

	params := &dto.SearchRbacRoleParams{
		Keywords: req.Keywords,
		Status:   req.Status,
	}
	roles, pa, err := ctr.rbacModel.SearchRoles(params, &paginate.Pager{
		Page: req.Page,
		Size: req.Size,
	})
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	ctr.l.Info("PA= %+v,%+v", pa, roles)

	ctr.Response(ctx, respdata.CSuccess.MData(respdata.SearchListResponse{
		List:     roles,
		Paginate: *pa,
	}))
}

// SimpleRoles godoc
// @Summary      角色管理-角色列表（简易+全部返回）
// @Description  修改个人信息
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.ListOptionReq   false  "查询参数,is_tree暂不支持"
// @success 201  {object} respdata.ResponseData{data=[]dto.RbacRoleSimple{}} "返回数据,is_simple=true"
// @failure 500 {object} respdata.ResponseData{} "返回失败"
// @Router       /mag/rbac/role/simples [post]
func (ctr *RbacController) SimpleRoles(ctx *gin.Context) {
	var req reqdata.ListOptionReq
	ctx.ShouldBind(&req)
	roles, err := ctr.rbacModel.Roles()
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	//简易返回
	ctr.Response(ctx, respdata.CSuccess.MData(utils.CopyMoreTo(roles, []*dto.RbacRoleSimple{})))
}

// SearchRolePermissions godoc
// @Summary      角色管理-角色权限列表
// @Description  修改个人信息
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.DetailReq   false  "指定role id"
// @success 200  {object} respdata.ResponseData{data=[]int64{}} "获取成功"
// @failure 500 {object} respdata.ResponseData{} "获取失败"
// @Router       /mag/rbac/role/permissions [post]
func (ctr *RbacController) SearchRolePermissions(ctx *gin.Context) {
	var req reqdata.DetailReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 && len(req.Ids) == 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	permIds, err := ctr.rbacModel.RolePermIds(req.Id, req.Ids...)
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.Response(ctx, respdata.CSuccess.MData(permIds))
}

// SaveRolePermissions godoc
// @Summary      角色管理-保存角色权限
// @Description  角色关联权限保存
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.SaveRolePermissionReq   false  "保存信息"
// @success 200  {object} respdata.ResponseData{data=[]int64{}} "操作成功"
// @failure 500 {object} respdata.ResponseData{} "获取失败"
// @Router       /mag/rbac/role/permission/save [post]
func (ctr *RbacController) SaveRolePermissions(ctx *gin.Context) {
	var req reqdata.SaveRolePermissionReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	err := ctr.rbacModel.DB().Transaction(func(tx *gorm.DB) error {
		return ctr.rbacModel.RoleRelatePerms(req.Id, req.Permissions)
	})
	if err != nil {
		ctr.l.Error("relate err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.Response(ctx, respdata.CSuccess)
}

// QueryRole godoc
// @Summary      角色管理-角色详情
// @Description  角色基本信息详情
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.DetailReq   false  "指定role id"
// @success 0  {object} respdata.ResponseData{data=models.RbacRole} "查询成功"
// @failure 4 {object} respdata.ResponseData{} "参数错误"
// @Router       /mag/rbac/role/detail [post]
func (ctr *RbacController) QueryRole(ctx *gin.Context) {
	var req reqdata.DetailReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	role, err := ctr.rbacModel.QueryRole(req.Id)
	if err != nil {
		ctr.l.Error("query err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	ctr.customizeLog(ctx, "查看角色-"+role.Name)

	ctr.Response(ctx, respdata.CSuccess.MData(role))
}

// SaveRole godoc
// @Summary      角色管理-角色新增/更新
// @Description  新增/更新角色
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.AddEditRoleReq   false  "指定role id"
// @success 0  {object} respdata.ResponseData{data=respdata.DetailResp} "操作成功"
// @failure 4 {object} respdata.ResponseData{} "参数错误"
// @Router       /mag/rbac/role/save [post]
func (ctr *RbacController) SaveRole(ctx *gin.Context) {
	var req reqdata.AddEditRoleReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 && (req.Name == "") {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	role := models.RbacRole{
		Id:       req.Id,
		Name:     req.Name,
		Describe: req.Describe,
		Sort:     req.Sort,
		Status:   req.Status,
	}

	var err error
	if req.Id > 0 {
		_, err = ctr.rbacModel.UpdateRole(req.Id, &role)
	} else {
		role.Id = 0
		_, err = ctr.rbacModel.AddRole(&role)
	}
	if err != nil {
		ctr.l.Error("add err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	if req.Id > 0 {
		ctr.customizeLog(ctx, "更新角色-"+role.Name)
	} else {
		ctr.customizeLog(ctx, "新增角色-"+role.Name)
	}

	ctr.Response(ctx, respdata.CSuccess.MData(respdata.DetailResp{Id: role.Id}))
}

// DisableRole godoc
// @Summary      角色管理-角色启用/禁用
// @Description  操作启用/禁用角色
// @Security     AdminKeyAuth
// @Tags         admin.rbac
// @Accept       json
// @Produce      json
// @Param        param    body     reqdata.SetStatusReq  true  "状态设置请求"
// @success 200  {object} respdata.ResponseData{} "设置成功"
// @Router       /mag/rbac/role/disable [post]
func (ctr *RbacController) DisableRole(ctx *gin.Context) {
	var req reqdata.SetStatusReq

	ctx.ShouldBind(&req)

	if req.Status != consts.StatusEnable && req.Status != consts.StatusDisable {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	role, err := ctr.rbacModel.QueryRole(req.Id)
	if err != nil {
		ctr.l.Error("operate fail %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	if role == nil {
		ctr.Response(ctx, respdata.CNotFound)
		return
	}
	_, err = ctr.rbacModel.ResetRoleStatus(req.Id, req.Status)
	if err != nil {
		ctr.l.Error("operate fail %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	if req.Status == consts.StatusEnable {
		ctr.customizeLog(ctx, "启用角色-"+role.Name)
	} else {
		ctr.customizeLog(ctx, "禁用角色-"+role.Name)
	}

	ctr.Response(ctx, respdata.CSuccess)
	return
}

// DeleteRole godoc
// @Summary      角色管理-角色删除
// @Description  操作删除角色
// @Security     AdminKeyAuth
// @Tags         admin.rbac
// @Accept       json
// @Produce      json
// @Param        param    body    reqdata.DetailReq   false  "指定role id"
// @success 200  {object} respdata.ResponseData{} "设置成功"
// @Router       /mag/rbac/role/delete [post]
func (ctr *RbacController) DeleteRole(ctx *gin.Context) {
	var req reqdata.DetailReq

	ctx.ShouldBind(&req)
	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}
	role, err := ctr.rbacModel.QueryRole(req.Id)
	if err != nil {
		ctr.l.Error("operate fail %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	if role == nil {
		ctr.Response(ctx, respdata.CNotFound)
		return
	}
	err = ctr.rbacModel.DeleteRole(req.Id)
	if err != nil {
		ctr.l.Error("operate fail %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.customizeLog(ctx, "删除角色-"+role.Name)

	ctr.Response(ctx, respdata.CSuccess)
	return
}

// SearchPermissions godoc
// @Summary      权限管理-权限树
// @Description  返回所有可操作的权限树
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.ListOptionReq   false  "查询参数"
// @success 0  {object} respdata.ResponseData{data=[]models.RbacPermission} "获取成功"
// @success 1  {object} respdata.ResponseData{data=[]dto.RbacPermissionSimple} "返回数据,is_simple=true"
// @success 2  {object} respdata.ResponseData{data=[]dto.RbacPermissionSimple} "返回树型数据,is_tree=true"
// @failure 100 {object} respdata.ResponseData{} "获取失败"
// @Router       /mag/rbac/permission/search [post]
func (ctr *RbacController) SearchPermissions(ctx *gin.Context) {
	var req reqdata.ListOptionReq
	ctx.ShouldBind(&req)
	list, err := ctr.rbacModel.Perms()
	if err != nil {
		ctr.l.Error("search err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	if req.IsSimple {
		ctr.customizeLog(ctx, "")
		ctr.Response(ctx, respdata.CSuccess.MData(utils.CopyMoreTo(list, []*dto.RbacPermissionSimple{})))
		return
	}
	if req.IsTree {
		ctr.customizeLog(ctx, "")
		tree := converts.PermissionSimpleToTree(utils.CopyMoreTo(list, []*dto.RbacPermissionSimple{}))
		ctr.Response(ctx, respdata.CSuccess.MData(tree))
		return
	}

	ctr.Response(ctx, respdata.CSuccess.MData(list))
}

// SavePermission godoc
// @Summary      权限管理-权限节点新增/更新
// @Description  权限节点新增/更新
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body    reqdata.AddEditPermissionReq   false  "提交信息"
// @success 200  {object} respdata.ResponseData{data=respdata.DetailResp} "操作成功"
// @success 500 {object} respdata.ResponseData{} "操作失败"
// @Router       /mag/rbac/permission/save [post]
func (ctr *RbacController) SavePermission(ctx *gin.Context) {
	var req reqdata.AddEditPermissionReq
	ctx.ShouldBind(&req)
	if req.Id <= 0 && (req.Name == "") {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	perm := models.RbacPermission{
		Id:       req.Id,
		Name:     req.Name,
		Describe: req.Describe,
		Icon:     req.Icon,
		Type:     req.Type,
		Path:     req.Path,
		Sort:     req.Sort,
		Pid:      req.Pid,
		Status:   req.Status,
		Remark:   "",
	}

	var err error
	if req.Id > 0 {
		_, err = ctr.rbacModel.UpdatePerm(req.Id, &perm)
	} else {
		perm.Id = 0
		_, err = ctr.rbacModel.AddPerm(&perm)
	}
	if err != nil {
		ctr.l.Error("add err %s", err)
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}

	ctr.Response(ctx, respdata.CSuccess.MData(respdata.DetailResp{Id: perm.Id}))
}

// DeletePermission godoc
// @Summary      权限管理-删除权限节点
// @Description  删除权限节点
// @Tags         admin.rbac
// @Produce      json
// @Security     AdminKeyAuth
// @Param        param    body     reqdata.DetailReq  true  "删除请求"
// @success 200  {object} respdata.ResponseData{} "返回数据"
// @success 500 {object} respdata.ResponseData{} "返回失败"
// @Router       /mag/rbac/permission/delete [post]
func (ctr *RbacController) DeletePermission(ctx *gin.Context) {
	var req reqdata.DetailReq
	ctx.ShouldBind(&req)

	if req.Id <= 0 {
		ctr.Response(ctx, respdata.CParamsInvalid)
		return
	}

	if err := ctr.rbacModel.DeletePerm(req.Id); err != nil {
		ctr.Response(ctx, respdata.CSysBusy)
		return
	}
	ctr.Response(ctx, respdata.CSuccess)
}

// CheckAccreditHandler 权限检查handler
func (ctr *RbacController) CheckAccreditHandler() gin.HandlerFunc {
	//生成权限map
	m := map[string]*dto.PermissionItem{}
	for _, v := range ctr.permissions {
		m[v.Path] = v
		for _, vv := range v.Paths {
			m[vv] = v
		}
	}
	//根据不同模式（mode）判断授权
	accredit := func(ctx *gin.Context, mode consts.AccreditMode) bool {
		auth := gin_http.GetAuth(ctx)
		//root用户不限权限

		if configstc.CommonExtendParams(auth.Extends).ExtendParamByBool("is_root") {
			return true
		}
		list, _ := ctr.rbacModel.UserPermIds(auth.Id) //用户权限
		path := "api:" + strings.TrimPrefix(ctx.Request.URL.Path, ctr.conf.RoutePrefix)
		//ctr.l.Info("Accredit %s", path)
		if find, exist := m[path]; exist {
			for _, v := range list {
				if v == find.Id {
					return true
				}
			}
		} else {
			return mode == consts.AccreditAllowWithIn
		}
		return false
	}
	return func(ctx *gin.Context) {
		if !accredit(ctx, consts.AccreditAllowWithIn) {
			ctr.Response(ctx, respdata.CPermissionDeny)
			ctx.Abort()
			return
		}
		ctx.Next()
	}
}

func (ctr *RbacController) Permissions() []*dto.PermissionItem {
	return ctr.permissions
}

// customizeLog 指定记录操作日志
func (ctr *RbacController) customizeLog(ctx *gin.Context, log string) {
	ctx.Set(consts.OperateLogContextKey, log)
}
